#!/usr/bin/env ruby

LKP_SRC ||= ENV['LKP_SRC'] || File.dirname(File.dirname(File.dirname(File.realpath($PROGRAM_NAME))))

require "#{LKP_SRC}/lib/statistics"
require "#{LKP_SRC}/lib/log"

module LKP
  class Stats
    def initialize
      @stats = {}
      @stats_array = {}
    end

    def key?(id)
      @stats.key? id
    end

    def empty?
      @stats.empty?
    end

    def [](id)
      @stats[id]
    end

    TEST_RESULT_MAP = {
      'ok' => 'pass',
      'passed' => 'pass',
      'success' => 'pass',
      'not_ok' => 'fail',
      'failure' => 'fail',
      'failed' => 'fail',
      'crash' => 'fail',
      'skipped' => 'skip',
      'not_run' => 'skip',
      'cancel' => 'skip',
      'expunged' => 'skip',
      'ignored_by_lkp' => 'skip',
    }

    # add functional result
    # - can add once only
    # - test_result could be success/pass/fail/skip/.. keywords; if nil, it's error id
    def add_test_error(context_line, test_case)
      add_test_result(context_line, test_case, nil)
    end

    def add_test_result(context_line, test_case, test_result)
      test_case = normalize(test_case)
      test_result = normalize(test_result).downcase if test_result

      case test_case
      when /^element\./
        unless @stats.key? test_case
          @stats[test_case] = test_result
        else
          @stats[test_case] += ' ' + test_result
        end
      else # convert to (eid|ts|tstage|msg|log|ttotal|tcase). prefix
        test_result = TEST_RESULT_MAP[test_result] if TEST_RESULT_MAP.key? test_result
        k = "#{test_case}.#{test_result}".chomp('.')
        if test_result.nil? || test_result =~ /fail|error|warn/
          type = 'eid'
          @stats["msg.#{k}.message"] = context_line
        else
          type = 'tcase'
        end
        @stats["#{type}.#{k}"] = '001'
      end
    end

    def add_id(id, val)
      id = normalize(id)
      $stderr.puts "#{$PROGRAM_NAME} stats warning: #{id} has already existed" if @stats.key?(id)
      check_stats_type(id)
      @stats[id] = val
    end

    # add performance metric
    def add_metric(id, val)
      add_id(id, val)
    end

    # append performance metric to ARRAY
    def append_metric(id, val)
      id = normalize(id)
      val = val.to_f if val.instance_of? String
      check_stats_type(id)
      @stats_array[id] ||= []
      @stats_array[id] << val
    end

    def result
      rollup
      @stats
    end

    def dump
      rollup
      @stats.each do |k, v|
        if k.start_with? 'msg.'
          puts "#{k}: #{v.strip.inspect}"
        else
          puts "#{k}: #{v}"
        end
      end
    end

    def rollup
      @stats_array.each do |k, v|
        begin
          case k
          when /^acc\./i
            @stats[k] = v.last - v.first
          when /^(cnt|ttotal)\./i
            @stats[k] = v.sum
          else
            @stats[k] = v.average
          end
        rescue
          puts "ERROR key = #{k}"
          puts "ERROR val = #{v}"
          raise
        end
      end
    end

    private

    def normalize(test_case)
      test_case.strip
               .gsub(/[\s,"_():]+/, '_')
               .gsub(/(^_+|_+$)/, '')
               .gsub(/_{2,}/, '_') # replace continuous _ to single _
               .gsub(/\.{4,}/, '.') # replace more than .... to single .
    end
  end
end
